package com.lin.issue.t.service;

import com.lin.issue.t.service.dao.DemoDeptDao;
import com.lin.issue.t.service.dao.DemoUserDao;
import com.lin.issue.t.service.entity.Dept;
import com.lin.issue.t.service.entity.User;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import javax.annotation.Resource;
import java.io.IOException;
import java.sql.SQLException;

/**
 * @author: linjiang
 * @date: 2025/7/21
 * 功能：测试spring事务的失效方式
 */
@Service
public class DemoTServiceImpl implements DemoTService {
    @Resource
    private DemoUserDao demoUserDao;
    @Resource
    private DemoDeptDao demoDeptDao;
    @Resource
    private JdbcTemplate jdbcTemplate;

    /**
     * 方式一：同类中非事务方法调用事务实例方法，导致事务失效
     * 失效原因：
     * spring事务是基于spring aop实现的。事务会使用代理对象。
     * 注意点：
     * 1.同类中，事务接口方法调用事务实例方法，无效实现效果。
     * 事务是基于aop实现的，所以自调用的方法上存在@Transactional注解也是无效的。
     * 2.为什么private修饰符导致事务失效
     * 事务是基于JDK或CGLIB动态代理实现的，private修饰的方法无法被代理。JDK是基于接口实现的，而接口默认都是public修饰的。
     */
    @Transactional
    @Override
    public void test01() throws SQLException {
        System.out.println("test01 Current transaction: " +
                TransactionSynchronizationManager.getCurrentTransactionName());
//        User user = new User("linjiang");
//        demoUserDao.insert(user);
//        this.test01_001();

        jdbcTemplate.execute("insert into sys_user(name) values('linjiang')");
        this.test01_002();
        // 模拟1
        throw new RuntimeException("测试事务");
    }

    public void test01_001() throws SQLException {
        System.out.println("test01_001 Current transaction: " +
                TransactionSynchronizationManager.getCurrentTransactionName());
        Dept dept = new Dept("java课程");
        demoDeptDao.insert(dept);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void test01_002() throws SQLException {
        System.out.println("test02 Current transaction: " +
                TransactionSynchronizationManager.getCurrentTransactionName()
        );

        jdbcTemplate.execute(
                "insert into sys_dept(name) values('java课程001')"
        );
    }

    /**
     * 方式二：异常捕获导致事务失效
     * 失效原因：
     * spring是根据异常的提醒，来对事务进行回滚，而事务被捕获，spring事务就不知道异常，从而无法回滚事务。
     */
    @Override
    @Transactional
    public void test02() throws SQLException {
        this.test01_002();
        try {
            User user = new User("linjiang");
            demoUserDao.insert(user);
            throw new RuntimeException("异常捕获！");
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    /**
     * 方式三：异常类型匹配导致事务失效
     * 失效原因：
     * Spring默认只在遇到未检查异常（RuntimeException及其子类）和Error时才回滚事务。源码地址:TransactionAspectSupport#completeTransactionAfterThrowing
     * 受检异常（如IOException、SQLException）被认为是业务异常，默认情况下不会触发事务回滚。
     * 解决方案：
     * 指定回滚的异常。
     */
    @Override
    @Transactional(rollbackFor = {IOException.class})
    public void test03() throws SQLException, IOException {
        this.test01_002();

        throw new IOException("数据库写入异常！");
    }


}
